/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.nhncorp.neptune.changelog;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;

import junit.framework.TestCase;

import com.nhncorp.neptune.common.conf.NConfiguration;
import com.nhncorp.neptune.common.testhelper.FaultManager;
import com.nhncorp.neptune.neptune_test.common.ForkableProcess;

public class TestChangeLogServerFailure extends TestCase {
  static final String TEST_STRING = "As recently as the 1990s, studies showed that most people preferred getting information from other people rather than from information retrieval (IR) systems. Of course, in that time period, most people also used human travel agents to book their travel. However, during the last decade, relentless optimization of information retrieval effectiveness has driven web search engines to new quality levels at which most people are satisfied most of the time, and web search has become a standard and often preferred source of information finding. For example, the 2004 Pew Internet Survey (Fallows 2004) found that “92% of Internet users say the Internet is a good place to go for getting everyday information.” To the surprise of many, the field of information retrieval has moved from being a primarily academic discipline to being the basis underlying most people’s preferred means of information access. This book presents the scientific underpinnings of this field, at a level accessible to graduate students as well as advanced undergraduates. Information retrieval did not begin with the Web. In response to various challenges of providing information access, the field of IR evolved to give principled approaches to searching various forms of content. The field began with scientific publications and library records but soon spread to other forms of content, particularly those of information professionals, such as journalists, lawyers, and doctors. Much of the scientific research on IR has occurred in these contexts, and much of the continued practice of IR deals with providing access to unstructured information in various corporate and governmental domains, and this work forms much of the foundation of our book.";
  static final int NUM_THREAD = 12;

  NConfiguration conf = new NConfiguration();
  ForkableProcess[] serverProcessList = new ForkableProcess[3];
  RpcUtil rpcUtil;
  FaultManager fm;

  protected void setUp() throws Exception {
    conf.set("testmode", "true");
    conf.set("neptune.changelog.filesystem", "pipe");
    conf.set("neptune.filesystem", "local");
    conf.set("neptune.local.temp", System.getProperty("user.home") + File.separator + ".neptune_local");

    fm = FaultManager.create(conf);
    fm.clearAllFaults();
    
    buildRpcUtil();

    for (int i = 0; i < serverProcessList.length; i++) {
      serverProcessList[i] = new ForkableProcess(
          "com.nhncorp.neptune.changelog.ChangeLogServer", String.valueOf(i), "true");

      final Method method = rpcUtil.buildRpcMethod("stopChangeLogServer");
      final int rpcIndex = i;

      serverProcessList[i].setStopMethod(new Runnable() {
        public void run() {
          try {
            rpcUtil.singleOnewayCall(method, rpcIndex);
          } catch (IOException e) {
          }
        }
      });

      serverProcessList[i].start();
    }

    sleep(10);

    formatChangeLogServers();
  }

  private void formatChangeLogServers() throws IOException {
    rpcUtil.multiParallelCall_v2(rpcUtil.buildRpcMethod("format"));
  }

  protected void tearDown() {
    System.out.println("#### tearDown ####");
    for (int i = 0; i < serverProcessList.length; i++) {
      try {
        serverProcessList[i].stop();
      } catch (Exception e) {
      }
    }
    sleep(5);

    fm.close();
  }

  private void buildRpcUtil() {
    InetSocketAddress[] rpcAddressList = new InetSocketAddress[3];

    for (int i = 0; i < rpcAddressList.length; i++) {
      rpcAddressList[i] = new InetSocketAddress("127.0.0.1", (57001 + i));
    }

    rpcUtil = new RpcUtil(conf, rpcAddressList);
  }

  public void testBasic_100() {
    System.out.println("testBasic_100()");
    ChangeLogClient fs = null;

    try {
      String tabletName = "SangchulTabletName";
      String txId = "HelloWorldSangchul";
      fs = new ChangeLogClient(conf, tabletName);
      fs.open();
      fs.startWriting(txId);
      fs.write(TEST_STRING.getBytes());
      int numWritten = fs.commitWriting();

      assertEquals(numWritten, fs.getLogFileSize());
      fs.close();
    } catch (IOException e) {
      e.printStackTrace();
      assertTrue(false);
    }

    if (fs != null)
      try {
        fs.removeAllLogs();
      } catch (IOException e) {
      }
  }

  public void testBasic_101() {
    System.out.println("testBasic_101()");
    new Test_101_runnable(0).run();
  }

  public void testBasic_101_mt() {
    System.out.println("testBasic_101_mt()");
    runThreadTest(Test_101_runnable.class);
  }

  public void testBasic_102() {
    System.out.println("testBasic_102()");
    new Test_102_runnable(0).run();
  }

  public void testBasic_102_mt() {
    System.out.println("testBasic_102_mt()");
    runThreadTest(Test_102_runnable.class);
  }

  public void testBasic_102_withDelay() {
    System.out.println("testBasic_102_withDelay()");

    /*
     * Pipe의 timeout 정책을 어떻게 할 것인지 결정한 다음
     * 다시 테스트 코드를 작성해야 한다.
     * 지금 현재 pipe는 120초 timeout을 사용하고 있는데, 이는 너무 길고,
     * 이 값은 configurable한데, ChangeLogClient의 timeout과
     * Pipe의 expiration timeout간의 관계에 대해 정리가 필요하다.
     */
    
    /*
    ChangeLogClient fs = null;
    try {
      int numWritten;
      String tabletName = "Sangchul_" + System.currentTimeMillis();
      String txId = "HelloWorldSangchul";
      fs = new ChangeLogClient(conf, tabletName);
      fs.open();
      fs.startWriting(txId);
      fs.write(TEST_STRING.getBytes());
      numWritten = fs.commitWriting();
      assertEquals(numWritten, fs.getLogFileSize());

      sleep(10);
      
      try {
        appendWithoutOpenClose(fs, numWritten, txId);
        assertTrue(false);
      } catch(IOException e) {
      }

    } catch (IOException e) {
      e.printStackTrace();
      assertTrue(false);
    } finally {
      if (fs != null) {
        try {
          fs.close();
        } catch(IOException e) {
        }
      }
    }

    if (fs != null) {
      try {
        fs.removeAllLogs();
      } catch (IOException e) {
      }
    }
    */
  }

  public void testBasic_103() {
    System.out.println("testBasic_103()");
    ChangeLogClient fs = null;

    try {
      String tabletName = "SangchulTabletName";
      String txId = "HelloWorldSangchul";
      fs = new ChangeLogClient(conf, tabletName);
      fs.open();
      fs.startWriting(txId);
      fs.write(TEST_STRING.getBytes());
      fs.commitWriting();

      sleep(2);

      fs.rollbackPosition = 0;
      fs.test_rollbackFileAtIndex(1);

      sleep(2);

      fs.close();
    } catch (IOException e) {
      e.printStackTrace();
      assertTrue(false);
    }

    sleep(2);

    try {
      fs.setConsistencyCheck(true);
      fs.open();
      assertTrue(false);
    } catch (UnmatchedLogException e) {
    } catch (Exception e) {
      e.printStackTrace();
      assertTrue(false);
    } finally {
      if (fs != null) {
        try {
          fs.removeAllLogs();
        } catch (IOException e) {
        }
      }
    }
  }

  public void testBasic_104() {
    System.out.println("testBasic_104()");
    serverProcessList[1].stop();
    sleep(1);
    ChangeLogClient fs = null;

    try {
      String tabletName = "SangchulTabletName";
      fs = new ChangeLogClient(conf, tabletName);
      fs.open();
      assertTrue(false);
    } catch (IOException e) {
      assertTrue(e.toString().startsWith("java.io.IOException: Fail to establish pipe connection"));
    }
  }

  public void testBasic_105() {
    System.out.println("testBasic_105()");
    ChangeLogClient fs = null;

    try {
      fm.inject(ListenerTestIF.class, "handleNewConnection", DeathFault.class);
      String tabletName = "SangchulTabletName";
      fs = new ChangeLogClient(conf, tabletName);
      fs.open();
      assertTrue(false);
    } catch (IOException e) {
      if (e.toString().startsWith("java.io.IOException: Fail to establish pipe connection") == false) {
        e.printStackTrace();
        assertTrue(false);
      }
    } catch (InterruptedException e) {
    }
  }

  public void testBasic_106() {
    System.out.println("testBasic_106()");
    ChangeLogClient fs = null;

    try {
      fm.inject(ListenerTestIF.class, "handleNewConnection", DelayFault.class);
      String tabletName = "SangchulTabletName";
      fs = new ChangeLogClient(conf, tabletName);
      fs.open();
      assertTrue(false);
    } catch (java.net.SocketTimeoutException e) {
    } catch (IOException e) {
      if (e.toString().indexOf("Fail to establish pipe connection") < 0) {
        e.printStackTrace();
        assertTrue(false);
      }
    } catch (InterruptedException e) {
    }
  }
  
  /*
  public void testBasic_107() {
    System.out.println("testBasic_107");
    
    ChangeLogClient fs = null;
    
    try {
      fm.inject(ListenerTestIF.class, "handleNewConnection", DeathFault.class);
      String tabletName = "SangchulTabletName";
      fs = new ChangeLogClient(conf, tabletName);
      fs.open();
      assertTrue(false);
    } catch (java.net.SocketTimeoutException e) {
    } catch (IOException e) {
      if (e.toString().indexOf("Fail to establish pipe connection") < 0) {
        e.printStackTrace();
        assertTrue(false);
      }
    }
  }
  */

  /*
   * public void testBasic_107() {
   * UniversalFaultMap.instance().registerFault(107, DeathFault.class);
   * 
   * ChangeLogFileSystem fs = null;
   * 
   * try { String tabletName = "SangchulTabletName"; fs = new
   * ChangeLogFileSystem(conf, tabletName); fs.open(); assertTrue(false); }
   * catch (java.net.SocketTimeoutException e) { } catch (IOException e) {
   * assertTrue(e.toString().startsWith(
   * "java.io.IOException: Fail to establish pipe connection")); } finally {
   * UniversalFaultMap.instance().removeFault(107); } }
   * 
   * public void testBasic_108() {
   * UniversalFaultMap.instance().registerFault(108, DeathFault.class);
   * 
   * ChangeLogFileSystem fs = null;
   * 
   * try { String tabletName = "SangchulTabletName"; fs = new
   * ChangeLogFileSystem(conf, tabletName); fs.open(); assertTrue(false); }
   * catch (java.net.SocketTimeoutException e) { } catch (IOException e) {
   * assertTrue(e.toString().startsWith(
   * "java.io.IOException: Fail to establish pipe connection")); } finally {
   * UniversalFaultMap.instance().removeFault(108); } }
   * 
   * public void testBasic_109() { ChangeLogFileSystem fs = null;
   * 
   * try { String tabletName = "SangchulTabletName"; String txId =
   * "HelloWorldSangchul"; fs = new ChangeLogFileSystem(conf, tabletName);
   * fs.open();
   * 
   * sleep(11);
   * 
   * fs.startWriting(txId); fs.write(TEST_STRING.getBytes());
   * fs.commitWriting(); assertTrue(false); } catch (IOException e) { try {
   * fs.getLogFileSize(); } catch (IOException e1) { e1.printStackTrace();
   * assertTrue(false); } } }
   * 
   * public void testBasic_110() {
   * UniversalFaultMap.instance().registerFault(110,
   * DeathFaultInReceiving.class);
   * 
   * ChangeLogFileSystem fs = null; byte[] bytes = TEST_STRING.getBytes(); try {
   * String tabletName = "SangchulTabletName"; String txId =
   * "HelloWorldSangchul"; fs = new ChangeLogFileSystem(conf, tabletName);
   * fs.open(); int median = bytes.length / 2; fs.startWriting(txId);
   * fs.write(bytes, 0, median); sleep(1); fs.write(bytes, median, bytes.length
   * - median); fs.commitWriting(); assertTrue(false); } catch (IOException e) {
   * try { fs.getLogFileSize(); } catch (IOException e1) { e1.printStackTrace();
   * assertTrue(false); } } finally {
   * UniversalFaultMap.instance().removeFault(110); } }
   * 
   * public void testBasic_111() {
   * UniversalFaultMap.instance().registerFault(111,
   * DelayFaultInReceiving.class);
   * 
   * ChangeLogFileSystem fs = null; byte[] bytes = TEST_STRING.getBytes(); try {
   * String tabletName = "SangchulTabletName"; String txId =
   * "HelloWorldSangchul"; fs = new ChangeLogFileSystem(conf, tabletName);
   * fs.open(); int median = bytes.length / 2; fs.startWriting(txId);
   * fs.write(bytes, 0, median); sleep(1); fs.write(bytes, median, bytes.length
   * - median); fs.commitWriting(); assertTrue(false); } catch (IOException e) {
   * try { fs.getLogFileSize(); } catch (IOException e1) { e1.printStackTrace();
   * assertTrue(false); } } finally {
   * UniversalFaultMap.instance().removeFault(111); if (fs != null) try {
   * fs.removeAllLogs(); } catch (IOException e) { } } }
   * 
   * public void testBasic_112() { ChangeLogFileSystem fs = null; byte[] bytes =
   * TEST_STRING.getBytes(); try { String tabletName = "SangchulTabletName";
   * String txId = "HelloWorldSangchul"; fs = new ChangeLogFileSystem(conf,
   * tabletName); fs.open(); int median = bytes.length / 2;
   * fs.startWriting(txId); fs.write(bytes, 0, median); sleep(11);
   * fs.write(bytes, median, bytes.length - median); fs.commitWriting();
   * assertTrue(false); } catch (IOException e) { try { fs.getLogFileSize(); }
   * catch (IOException e1) { e1.printStackTrace(); assertTrue(false); } }
   * finally { if (fs != null) try { fs.removeAllLogs(); } catch (IOException e)
   * { } } }
   */

  /*
   * 테스트 케이스 보강 후 다시 작성하기 public void testBasic_113() {
   * 
   * ChangeLogFileSystem fs = null; int numWritten = 0; String tabletName =
   * "SangchulTabletName"; String txId1 = "HelloWorldSANGCHUL"; String txId2 =
   * "HelloWorldsangchul";
   * 
   * try { fs = new ChangeLogFileSystem(conf, tabletName); fs.open();
   * fs.startWriting(txId1); fs.write(TEST_STRING.getBytes()); numWritten =
   * fs.commitWriting(); assertEquals(numWritten, fs.getLogFileSize()); } catch
   * (IOException e) { e.printStackTrace(); assertTrue(false); }
   * 
   * try { UniversalFaultMap.instance().registerFault(113, DelayFault.class);
   * 
   * fs.startWriting(txId2); fs.write(TEST_STRING.getBytes());
   * fs.commitWriting(); assertTrue(false); } catch (IOException e) { try {
   * assertEquals(numWritten, fs.getLogFileSize()); } catch (IOException e1) {
   * e1.printStackTrace(); assertTrue(false); } } finally {
   * UniversalFaultMap.instance().removeFault(113); }
   * 
   * if (fs != null) try { fs.removeAllLogs(); } catch(IOException e) {
   * e.printStackTrace(); } }
   * 
   * public void testBasic_114() {
   * 
   * ChangeLogFileSystem fs = null; int numWritten = 0; String tabletName =
   * "SangchulTabletName"; String txId1 = "HelloWorldSANGCHUL"; String txId2 =
   * "HelloWorldsangchul";
   * 
   * try { fs = new ChangeLogFileSystem(conf, tabletName); fs.open();
   * fs.startWriting(txId1); fs.write(TEST_STRING.getBytes()); numWritten =
   * fs.commitWriting(); assertEquals(numWritten, fs.getLogFileSize()); } catch
   * (IOException e) { e.printStackTrace(); assertTrue(false); }
   * 
   * try { UniversalFaultMap.instance().registerFault(114,
   * IOExceptionFault.class);
   * 
   * fs.startWriting(txId2); fs.write(TEST_STRING.getBytes());
   * fs.commitWriting(); assertTrue(false); } catch (IOException e) { try {
   * assertEquals(numWritten, fs.getLogFileSize()); } catch (IOException e1) {
   * e1.printStackTrace(); assertTrue(false); } } finally {
   * UniversalFaultMap.instance().removeFault(114); }
   * 
   * if (fs != null) try { fs.removeAllLogs(); } catch(IOException e) {
   * e.printStackTrace(); } }
   * 
   * public void testBasic_114_death() {
   * 
   * ChangeLogFileSystem fs = null; int numWritten = 0; String tabletName =
   * "SangchulTabletName"; String txId1 = "HelloWorldSANGCHUL"; String txId2 =
   * "HelloWorldsangchul";
   * 
   * try { fs = new ChangeLogFileSystem(conf, tabletName); fs.open();
   * fs.startWriting(txId1); fs.write(TEST_STRING.getBytes()); numWritten =
   * fs.commitWriting(); assertEquals(numWritten, fs.getLogFileSize()); } catch
   * (IOException e) { e.printStackTrace(); assertTrue(false); }
   * 
   * try { UniversalFaultMap.instance().registerFault(114, DeathFault.class);
   * 
   * fs.startWriting(txId2); fs.write(TEST_STRING.getBytes());
   * fs.commitWriting(); assertTrue(false); } catch (IOException e) { try {
   * assertEquals(numWritten, fs.getLogFileSize()); } catch (IOException e1) {
   * e1.printStackTrace(); assertTrue(false); } } finally {
   * UniversalFaultMap.instance().removeFault(114); }
   * 
   * if (fs != null) try { fs.removeAllLogs(); } catch(IOException e) {
   * e.printStackTrace(); }
   * 
   * cleanupLogFiles(tabletName); }
   * 
   * private void cleanupLogFiles(String tabletName) { File dir = new
   * File(ChangeLogFileChannel.CHANGELOG_PATH + tabletName); String[] list =
   * dir.list(); for(String fileName : list) { File logFile = new File(dir,
   * fileName); logFile.delete(); } }
   * 
   * public void testBasic_115() {
   * 
   * ChangeLogFileSystem fs = null; int numWritten = 0; String tabletName =
   * "SangchulTabletName"; String txId1 = "HelloWorldSANGCHUL"; String txId2 =
   * "HelloWorldsangchul";
   * 
   * try { fs = new ChangeLogFileSystem(conf, tabletName); fs.open();
   * fs.startWriting(txId1); fs.write(TEST_STRING.getBytes()); numWritten =
   * fs.commitWriting(); assertEquals(numWritten, fs.getLogFileSize()); } catch
   * (IOException e) { e.printStackTrace(); assertTrue(false); }
   * 
   * try { UniversalFaultMap.instance().registerFault(115, DelayFault.class);
   * 
   * fs.startWriting(txId2); fs.write(TEST_STRING.getBytes());
   * fs.commitWriting(); assertTrue(false); } catch (IOException e) { try {
   * assertEquals(numWritten, fs.getLogFileSize()); } catch (IOException e1) {
   * e1.printStackTrace(); assertTrue(false); } } finally {
   * UniversalFaultMap.instance().removeFault(115); }
   * 
   * if (fs != null) try { fs.removeAllLogs(); } catch(IOException e) {
   * e.printStackTrace(); } }
   * 
   * / public void testBasic_116() { ChangeLogFileSystem fs = null;
   * 
   * try { String tabletName = "SangchulTabletName"; String[] txIdList =
   * {"HelloWorldSANGCHUL", "HelloWorldsangchul", "HelloWorldSangchul"};
   * 
   * fs = new ChangeLogFileSystem(conf, tabletName); fs.open();
   * fs.startWriting(txIdList[0]); fs.write(TEST_STRING.getBytes()); int
   * numWritten = fs.commitWriting();
   * 
   * assertEquals(numWritten, fs.getLogFileSize());
   * 
   * fs.startWriting(txIdList[1]); fs.write(TEST_STRING.getBytes()); numWritten
   * += fs.commitWriting();
   * 
   * fs.startWriting(txIdList[2]); fs.write(TEST_STRING.getBytes()); numWritten
   * += fs.commitWriting();
   * 
   * fs.close();
   * 
   * TestChangeLog[] logList = fs.readLastLog();
   * 
   * assertEquals(3, logList.length);
   * 
   * for(int i = 0; i < logList.length; i++) { assertEquals(i, logList[i].seq);
   * assertEquals(txIdList[i], new String(logList[i].id));
   * assertEquals(TEST_STRING, new String(logList[i].content)); } } catch
   * (IOException e) { e.printStackTrace(); assertTrue(false); }
   * 
   * if (fs != null) try { fs.removeAllLogs(); } catch(IOException e) {} }
   * 
   * public void testBasic_117() { ChangeLogFileSystem fs = null;
   * 
   * try { String tabletName = "SangchulTabletName"; String[] txIdList =
   * {"HelloWorldSANGCHUL", "HelloWorldsangchul", "HelloWorldSangchul"};
   * 
   * fs = new ChangeLogFileSystem(conf, tabletName); fs.open();
   * fs.startWriting(txIdList[0]); fs.write(TEST_STRING.getBytes()); int
   * numWritten = fs.commitWriting();
   * 
   * assertEquals(numWritten, fs.getLogFileSize());
   * 
   * fs.startWriting(txIdList[1]); fs.write(TEST_STRING.getBytes()); numWritten
   * += fs.commitWriting();
   * 
   * fs.startWriting(txIdList[2]); fs.write(TEST_STRING.getBytes()); numWritten
   * += fs.commitWriting();
   * 
   * fs.close();
   * 
   * truncate(tabletName, "ChangeLog.18000", numWritten / 3 2);
   * 
   * TestChangeLog[] logList = fs.readLastLog();
   * 
   * assertEquals(2, logList.length);
   * 
   * for(int i = 0; i < logList.length; i++) { assertEquals(i, logList[i].seq);
   * assertEquals(txIdList[i], new String(logList[i].id));
   * assertEquals(TEST_STRING, new String(logList[i].content)); } } catch
   * (IOException e) { e.printStackTrace(); assertTrue(false); }
   * 
   * if (fs != null) try { fs.removeAllLogs(); } catch(IOException e) {} }
   * 
   * public void testBasic_118() { ChangeLogFileSystem fs = null;
   * 
   * try { String tabletName = "SangchulTabletName"; String[] txIdList =
   * {"HelloWorldSANGCHUL", "HelloWorldsangchul", "HelloWorldSangchul"};
   * 
   * fs = new ChangeLogFileSystem(conf, tabletName); fs.open();
   * fs.startWriting(txIdList[0]); fs.write(TEST_STRING.getBytes()); int
   * numWritten = fs.commitWriting();
   * 
   * fs.backupLogFile(); assertEquals(0, fs.getLogFileSize());
   * 
   * sleep(5);
   * 
   * fs.startWriting(txIdList[1]); fs.write(TEST_STRING.getBytes()); numWritten
   * += fs.commitWriting();
   * 
   * fs.backupLogFile(); assertEquals(0, fs.getLogFileSize());
   * 
   * sleep(5);
   * 
   * fs.startWriting(txIdList[2]); fs.write(TEST_STRING.getBytes()); numWritten
   * += fs.commitWriting();
   * 
   * fs.close();
   * 
   * TestChangeLog[] logList = fs.readLastLog();
   * 
   * assertEquals(1, logList.length);
   * 
   * assertEquals(2, logList[0].seq); assertEquals(txIdList[2], new
   * String(logList[0].id)); assertEquals(TEST_STRING, new
   * String(logList[0].content));
   * 
   * sleep(5);
   * 
   * logList = fs.readAllLogs(); assertEquals(3, logList.length);
   * 
   * Arrays.sort(logList);
   * 
   * for(int i = 0; i < logList.length; i++) { assertEquals(i, logList[i].seq);
   * assertEquals(txIdList[i], new String(logList[i].id));
   * assertEquals(TEST_STRING, new String(logList[i].content)); } } catch
   * (IOException e) { e.printStackTrace(); assertTrue(false); }
   * 
   * if (fs != null) try { fs.removeAllLogs(); } catch(IOException e) {} } /
   */
  // // private methods ////
  private void truncate(String parentDir, String fileName, int size) {
    /*
     * try { FileChannel fc = new
     * RandomAccessFile(ChangeLogFileChannel.CHANGELOG_PATH + parentDir +
     * File.separator + fileName, "rw").getChannel(); fc.truncate(size);
     * fc.force(false); fc.close(); } catch (Exception e) { e.printStackTrace();
     * assertTrue(false); }
     */
  }

  class Test_101_runnable implements Runnable {
    int num;

    public Test_101_runnable(int i) {
      this.num = i;
    }

    public void run() {
      ChangeLogClient fs = null;
      try {
        int numWritten;
        String tabletName = "Sangchul_" + num;
        String txId = "HelloWorldSangchul";
        fs = new ChangeLogClient(conf, tabletName);
        fs.open();
        fs.startWriting(txId);
        fs.write(TEST_STRING.getBytes());
        numWritten = fs.commitWriting();
        fs.close();
        assertEquals(numWritten, fs.getLogFileSize());

        numWritten = appendWithOpenClose(fs, numWritten, txId);
        numWritten = appendWithOpenClose(fs, numWritten, txId);
        numWritten = appendWithOpenClose(fs, numWritten, txId);

      } catch (Exception e) {
        e.printStackTrace();
        assertTrue(false);
      }

      if (fs != null)
        try {
          fs.removeAllLogs();
        } catch (IOException e) {
        }
    }
  };

  class Test_102_runnable implements Runnable {
    int num;
    int sleepSec = 0;

    public Test_102_runnable(int i) {
      this.num = i;
    }

    public void setDelayBeforeAppend(int sec) {
      this.sleepSec = sec;
    }

    public void run() {
      ChangeLogClient fs = null;
      try {
        int numWritten;
        String tabletName = "Sangchul_" + num;
        String txId = "HelloWorldSangchul";
        fs = new ChangeLogClient(conf, tabletName);
        fs.open();
        fs.startWriting(txId);
        fs.write(TEST_STRING.getBytes());
        numWritten = fs.commitWriting();
        assertEquals(numWritten, fs.getLogFileSize());

        sleep(sleepSec);
        numWritten = appendWithoutOpenClose(fs, numWritten, txId);
        sleep(sleepSec);
        numWritten = appendWithoutOpenClose(fs, numWritten, txId);
        sleep(sleepSec);
        numWritten = appendWithoutOpenClose(fs, numWritten, txId);

        fs.close();
      } catch (IOException e) {
        e.printStackTrace();
        assertTrue(false);
      }

      if (fs != null) {
        try {
          fs.removeAllLogs();
        } catch (IOException e) {
        }
      }
    }
  };

  private int appendWithOpenClose(ChangeLogClient fs, int numWritten, String txId)
      throws IOException {
    sleep(1);

    fs.open();
    fs.startWriting(txId);
    fs.write(TEST_STRING.getBytes());
    numWritten += fs.commitWriting();
    fs.close();

    assertEquals(numWritten, fs.getLogFileSize());
    return numWritten;
  }

  private int appendWithoutOpenClose(ChangeLogClient fs, int numWritten, String txId)
      throws IOException {
    sleep(1);

    fs.startWriting(txId);
    fs.write(TEST_STRING.getBytes());
    numWritten += fs.commitWriting();

    assertEquals(numWritten, fs.getLogFileSize());
    return numWritten;
  }

  private void sleep(int sec) {
    if (sec > 0) {
      System.out.println("#### SLEEP " + sec + " sec ####");
      try {
        Thread.sleep(sec * 1000);
      } catch (Exception e) {
      }
    }
  }

  private void runThreadTest(Class<? extends Runnable> runnableClass) {
    Thread[] threadList = new Thread[NUM_THREAD];

    try {
      Constructor<? extends Runnable> cons = runnableClass.getConstructor(this.getClass(),
          int.class);
      for (int i = 0; i < threadList.length; i++) {
        threadList[i] = new Thread(cons.newInstance(this, i));
        threadList[i].start();
      }
    } catch (Exception e) {
      e.printStackTrace();
      assertTrue(false);
    }

    for (int i = 0; i < threadList.length; i++) {
      try {
        threadList[i].join();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  public static void main(String[] args) {
    junit.textui.TestRunner.run(TestChangeLogServerFailure.class);
  }
}

class FaultConstants {
  public static final int FAULT_SERVER_PORT = 18001;
}

/*
 * class DeathFaultInReceiving extends Fault { public DeathFaultInReceiving() {
 * }
 * 
 * public boolean doRun(Object... arg) { if (arg.length != 2) {
 * System.err.println("what the hell?"); return false; }
 * 
 * return ((Integer) arg[0]) == FaultConstants.FAULT_SERVER_PORT && ((Integer)
 * arg[1]) > TestChangeLogServerFailure.TEST_STRING.length() / 2; }
 * 
 * public String getFaultDescription() { return
 * "Kill Change Log server at port " + FaultConstants.FAULT_SERVER_PORT; }
 * 
 * public void run(Object... arg) { System.exit(-1); } }
 * 
 * class DelayFault extends Fault { public DelayFault() { }
 * 
 * public String getFaultDescription() { return "sleep 10 sec"; }
 * 
 * public void run(Object... arg) { try { Thread.sleep(10000); } catch
 * (InterruptedException e) { } }
 * 
 * public boolean doRun(Object... arg) { return true; } }
 * 
 * class DelayFaultInReceiving extends Fault { public DelayFaultInReceiving() {
 * }
 * 
 * public String getFaultDescription() { return "sleep 10 sec"; }
 * 
 * public void run(Object... arg) { try { Thread.sleep(10000); } catch
 * (InterruptedException e) { } }
 * 
 * public boolean doRun(Object... arg) { if (arg.length != 2) {
 * System.err.println("what the hell?"); return false; }
 * 
 * return ((Integer) arg[0]) == FaultConstants.FAULT_SERVER_PORT && ((Integer)
 * arg[1]) > TestChangeLogServerFailure.TEST_STRING.length() / 2; } }
 * 
 * class IOExceptionFault extends Fault {
 * 
 * public boolean doRun(Object... arg) { if (arg.length != 1) {
 * System.err.println("what the hell?"); return false; }
 * 
 * return ((Integer) arg[0]) == FaultConstants.FAULT_SERVER_PORT; }
 * 
 * public void run(Object... arg) throws IOException { throw new
 * IOException("test"); }
 * 
 * }
 */
